Ethereum 系统架构梳理

来源: ethereum.org · 更新: 2024

1. 系统架构总览

Ethereum 采用双层架构:执行层(Execution Layer)处理交易和执行智能合约,共识层(Consensus Layer / Beacon Chain)负责区块排序和 PoS 共识。两层通过 Engine API 通信。

1.1 双层架构总览

graph TB subgraph Users["👤 用户 / DApp"] Wallet["钱包 (MetaMask / Rabby)"] DApp["DApp 前端"] end subgraph EL["⚙️ 执行层 (Execution Layer)"] direction TB JSONRPC["JSON-RPC API"] TxPool["交易内存池 (Mempool)"] EVM["EVM 虚拟机"] StateDB[("状态数据库
(Merkle Patricia Trie)")] JSONRPC --> TxPool TxPool --> EVM EVM --> StateDB end subgraph CL["🔗 共识层 (Consensus Layer / Beacon Chain)"] direction TB FC["分叉选择 (LMD-GHOST)"] FFG["最终确定性 (Casper FFG)"] RANDAO["RANDAO 随机数"] Validators["验证者管理"] end subgraph Net["🌐 P2P 网络"] p2p["libp2p / devp2p"] end Wallet -->|"签名交易"| JSONRPC DApp -->|"eth_call / eth_sendTransaction"| JSONRPC JSONRPC <-->|"Engine API
newPayload / forkchoiceUpdated"| FC EL <-->|"区块 & 交易 gossip"| p2p CL <-->|"见证 & 区块 gossip"| p2p

1.2 核心数据流

flowchart LR A["用户签名交易"] --> B["执行客户端
验证 & 入 Mempool"] B --> C["区块提议者
打包交易"] C --> D["执行交易
生成新状态根"] D --> E["共识客户端
封装 Beacon Block"] E --> F["P2P 广播"] F --> G["其他验证者
重新执行 & 验证"] G --> H["投见证票
(Attestation)"] H --> I["达到 2/3 多数"] I --> J["区块最终确定
(Finalized)"]

2. 账户模型

Ethereum 有两种账户类型:外部账户 (EOA) 由私钥控制,合约账户 由代码控制。每个账户是一个包含 4 个字段的状态对象。

2.1 账户结构

classDiagram class Account { +uint64 nonce "交易计数器(EOA) 或 合约创建数(Contract)" +uint256 balance "余额 (单位: wei, 1 ETH = 10^18 wei)" +bytes32 storageRoot "存储 Trie 根哈希 (合约专用)" +bytes32 codeHash "代码哈希 (EOA 为空串哈希)" } class EOA { 由私钥控制 可发起交易 创建免费 无 storage } class ContractAccount { 由字节码控制 只能响应交易 创建需要 Gas 有 storage } class KeyPair { +string privateKey "64 hex 字符" +string publicKey "ECDSA 公钥" +string address "公钥 Keccak256 后取后 20 字节" } EOA --|> Account : "nonce, balance, codeHash=KEC(EMPTY)" ContractAccount --|> Account : "nonce, balance, storageRoot, codeHash" EOA *-- KeyPair

2.2 地址派生

flowchart LR A["随机私钥
64 hex"] --> B["ECDSA secp256k1"] B --> C["公钥
128 hex (未压缩)"] C --> D["Keccak-256 哈希"] D --> E["取后 20 字节"] E --> F["地址
0x + 40 hex"] G["创建者地址 + nonce"] --> H["RLP 编码"] H --> I["Keccak-256"] I --> J["取后 20 字节"] J --> K["合约地址
0x + 40 hex"]

3. 交易生命周期

一笔交易从用户签名到最终确定,经历 Mempool、执行、区块打包、共识投票、最终确定等阶段。

3.1 交易字段

classDiagram class Transaction { +address from "发送者 (由签名恢复)" +address to "接收者 (合约创建时为空)" +uint256 value "转账金额 (wei)" +bytes data "调用数据 / 合约 init code" +uint64 nonce "发送者交易序号" +uint256 gasLimit "最大 Gas 消耗量" +uint256 maxFeePerGas "最高单价 (EIP-1559)" +uint256 maxPriorityFeePerGas "给验证者的小费" +bytes chainId "链 ID,防重放" +bytes signature "ECDSA 签名 (v, r, s)" }

3.2 交易生命周期时序

sequenceDiagram actor U as 用户 (Wallet) participant EL as 执行客户端 participant MP as Mempool participant Prop as 区块提议者 (Validator) participant CL as 共识客户端 participant Att as 见证委员会 U->>U: 1. 用私钥签名交易 U->>EL: 2. eth_sendRawTransaction EL->>EL: 3. 验证签名 & nonce & 余额 EL->>MP: 4. 入本地 Mempool EL-->>EL: 5. gossip 到其他执行客户端 Note over Prop: 6. RANDAO 随机选出
当前 slot 的区块提议者 Prop->>MP: 7. 从 Mempool 拉取交易 Prop->>EL: 8. 执行交易,生成新的状态根 Prop->>CL: 9. 封装 Beacon Block CL->>CL: 10. gossip 到共识网络 Att->>EL: 11. 重新执行交易验证 Att->>CL: 12. 投见证票 (Attestation) Note over CL,Att: 13. 一个 epoch (32 slots) 内
收集超 2/3 见证票 CL->>CL: 14. Casper FFG: 检查点 finalize Note over U: 交易最终确定 ✅

3.3 Gas 费用模型 (EIP-1559)

flowchart TB subgraph FeeCalc["Gas 计算"] GasUsed["实际消耗 Gas"] --> TotalFee BaseFee["基础费用 (Base Fee)"] -->|"自动调整,会销毁"| TotalFee Tip["小费 (Priority Fee)"] -->|"归验证者"| TotalFee TotalFee["总费用 = GasUsed × (BaseFee + Tip)"] end subgraph BaseFeeAdjust["基础费用调整"] direction TB PrevBlock["上一区块"] -->|"超过目标 50% 满"| Inc["+12.5% 基础费"] PrevBlock -->|"低于目标 50% 满"| Dec["-12.5% 基础费"] PrevBlock -->|"恰好 50% 满"| Same["不变"] end

4. ERC-20 代币机制

Ethereum 协议层只认 ETH——账户的 balance 字段只记录该地址的 ETH 余额。USDT、USDC、DAI 等代币并非原生的,而是由 ERC-20 合约的 storage 维护的账本。

用户"转账 USDT",是发送一笔 value=0 ETH 的交易到 USDT 合约地址——用户不需要持有 ETH,只需要少量 ETH 付 gas 费。to 填的是合约地址而非收款方,收款方和金额都编码在交易的 data 字段里。

4.1 原生 ETH 转账 vs ERC-20 代币转账

graph TB subgraph Native["ETH 原生转账"] N1["from: 用户A
to: 用户B
value: 1 ETH
data: 0x (空)"] N2["EVM 直接从 A.balance -= 1
B.balance += 1"] end subgraph Token["ERC-20 代币转账"] T1["from: 用户A
to: USDT合约 (0xdAC1...)
value: 0 ETH
data: 0xa9059cbb...
└─ transfer(address,uint256) 的编码"] T2["EVM 调用 USDT 合约 transfer()"] T3["合约 storage 更新:
balanceOf[A] -= 100
balanceOf[B] += 100"] end N1 --> N2 T1 --> T2 --> T3

4.2 data 字段编码 (ABI Encoding)

graph LR subgraph Encoding["data 字段拆解 (32 字节 = 64 hex)"] Selector["4 字节
函数选择器
0xa9059cbb"] Param1["32 字节
recipient (address)
0x000...接收地址"] Param2["32 字节
amount (uint256)
0x000...64"] end Step1["transfer(address recipient, uint256 amount)"] -->|"Keccak256 前 4 字节"| Selector Step2["接收地址 20 字节"] -->|"左侧补 0 至 32 字节"| Param1 Step3["金额 100 (USDT 6 位小数) = 100000000"] -->|"uint256 编码"| Param2

4.3 ERC-20 合约标准接口

classDiagram class IERC20 { +totalSupply() uint256 +balanceOf(address) uint256 +transfer(address to, uint256 amount) bool +allowance(address owner, address spender) uint256 +approve(address spender, uint256 amount) bool +transferFrom(address from, address to, uint256 amount) bool } class ERC20_Events { event Transfer(address indexed from, address indexed to, uint256 value) event Approval(address indexed owner, address indexed spender, uint256 value) } class ERC20_Storage { mapping balanceOf: address → uint256 "各地址的持币量" mapping allowance: address → mapping → uint256 "授权额度" } IERC20 ..> ERC20_Events : "emit" IERC20 ..> ERC20_Storage : "读写"

4.4 approve + transferFrom 授权流程 (DeFi 基础)

sequenceDiagram actor U as 用户 (EOA) participant Token as USDT 合约 participant DeFi as Aave / Uniswap 合约 Note over U,DeFi: ▸ 第 1 步: approve (授权) U->>Token: approve(Aave合约, 1000 USDT) Token->>Token: allowance[U][Aave] = 1000 Token-->>U: emit Approval(U, Aave, 1000) Note over U,DeFi: ▸ 第 2 步: 调用 DeFi 协议 U->>DeFi: supply(1000 USDT) 等 DeFi->>Token: transferFrom(U, DeFi, 1000 USDT) Token->>Token: 检查 allowance[U][DeFi] >= 1000 Token->>Token: balanceOf[U] -= 1000
balanceOf[DeFi] += 1000 Token-->>DeFi: return true Note over U,DeFi: 所以往 Aave 存 USDT 需要两笔交易:
1. approve USDT 给 Aave
2. supply 到 Aave

4.5 代币余额查询 (eth_call)

sequenceDiagram participant Wallet as 钱包 / DApp participant Node as 执行节点 Note over Wallet,Node: 代币余额不在 account.balance 里
必须调用合约的 balanceOf() Wallet->>Node: eth_call
to: USDT合约
data: balanceOf(用户地址) Node->>Node: 执行 balanceOf()
从合约 storage 读取 Node-->>Wallet: 0x000...000064 (100 USDT) Note over Wallet,Node: eth_call 是只读查询
不消耗 Gas,不上链

4.6 UserA 转账 10 USDT 给 UserB 完整时序

sequenceDiagram actor A as UserA (EOA) actor B as UserB (EOA) participant W as Wallet (MetaMask) participant Node as 执行节点 participant USDT as USDT 合约
0xdAC17F958D2ee523a2206206994597C13D831ec7 participant Miner as 区块提议者 participant Chain as Ethereum 链 Note over A,B: UserA 要给 UserB 转 10 USDT Note over W: 钱包维护 token名→合约地址 映射:
"USDT" → 0xdAC17F958D2ee523a2206206994597C13D831ec7
用户看到的 "USDT" 只是钱包 UI 的别名 A->>W: 输入 UserB 地址 + 10 USDT,点发送 W->>W: 钱包查映射表,把 "USDT" 翻译成
合约地址 0xdAC17F...
构造交易对象:
from: UserA
to: USDT合约地址 (0xdAC17F...)
value: 0 ETH
data: encode(transfer(UserB, 10*10^6)) W->>W: 估算 Gas Limit (~50,000) W->>A: 展示 Gas 费用,请确认 A->>W: 确认签名 W->>W: 用 UserA 私钥 ECDSA 签名 W->>Node: eth_sendRawTransaction(签名后的交易) Node->>Node: 验证: 签名 ✓ nonce ✓ ETH 余额够付 gas ✓ Node->>Node: 入本地 Mempool Node-->>Node: gossip 到其他节点 Miner->>Node: 从 Mempool 取出此交易 Miner->>USDT: CALL transfer(UserB, 10,000,000) USDT->>USDT: require(balanceOf[UserA] >= 10,000,000) USDT->>USDT: balanceOf[UserA] -= 10,000,000 USDT->>USDT: balanceOf[UserB] += 10,000,000 USDT-->>Miner: emit Transfer(UserA, UserB, 10,000,000) USDT-->>Miner: return true Miner->>Miner: 结算 Gas = GasUsed × (BaseFee + Tip) Miner->>Miner: 封装区块,广播 Chain->>Chain: 见证委员会验证 → Finalize Note over A,B: UserB 查看钱包: +10 USDT ✓
整个过程 UserB 无需任何操作

5. EVM 执行模型

EVM 是一个基于栈的虚拟机,栈深度 1024,每个元素为 256-bit 字。编译后的智能合约字节码被解析为 Opcodes 逐条执行。

5.1 EVM 内部结构

graph TB subgraph EVM_Internal["EVM 执行环境"] PC["程序计数器 (PC)"] Stack["操作栈
深度 1024 × 256-bit"] Memory["暂存内存
(Volatile, 交易间不持久)"] TStore["瞬态存储
(TSTORE/TLOAD, 跨内部调用)"] Gas["Gas 计数器"] end subgraph World["外部世界状态"] AccountInfo["账户信息
(Balance, Nonce)"] BlockInfo["区块信息
(Timestamp, Number, GasLimit)"] Storage["持久存储
(SSTORE/SLOAD, 写入全局状态)"] end subgraph Bytecode["合约字节码"] Opcodes["PUSH1, ADD, SSTORE, CALL, RETURN..."] end PC --> Opcodes Opcodes -->|"操作数"| Stack Opcodes -->|"读写"| Memory Opcodes -->|"TSTORE/TLOAD"| TStore Opcodes -->|"SSTORE/SLOAD"| Storage Opcodes -->|"BALANCE/CALL"| AccountInfo Opcodes -->|"TIMESTAMP/NUMBER"| BlockInfo Opcodes -->|"消耗"| Gas

5.2 状态转换函数

flowchart LR subgraph Inputs["输入"] S["旧状态 S
(World State Trie)"] T["交易列表 T"] end subgraph Transition["状态转换 Y"] Y["Y(S, T) = S'"] end subgraph Outputs["输出"] S2["新状态 S'
(New State Root)"] Receipts["交易收据
(Logs, Gas Used)"] end Inputs --> Transition Transition --> Outputs

5.3 合约调用机制

sequenceDiagram participant EOA as 外部账户 (EOA) participant EVM as EVM participant CA as 合约 A participant CB as 合约 B EOA->>EVM: 发起 Transaction EVM->>CA: CALL (value, data) activate CA CA->>CA: 执行逻辑 CA->>EVM: CALL 合约 B EVM->>CB: CALL (value, data) activate CB CB->>CB: 执行逻辑 CB-->>EVM: RETURN deactivate CB CA->>CA: 继续执行 CA-->>EVM: RETURN deactivate CA EVM->>EVM: 结算 Gas Note over EVM: 调用深度上限 1024
Gas 限制 63/64 规则

6. PoS 共识流程 (Gasper)

Ethereum 的共识协议称为 Gasper,由两部分组成:Casper FFG(最终确定性工具)和 LMD-GHOST(分叉选择规则)。时间以 Slot(12 秒)和 Epoch(32 个 Slot,6.4 分钟)为单位。

6.1 Slot & Epoch 时间结构

graph LR subgraph Epoch["一个 Epoch (32 slots = 6.4 分钟)"] direction LR S0["Slot 0
(Checkpoint)"] --> S1["Slot 1"] --> Dots["..."] --> S31["Slot 31"] end S31 -->|"下一个 Epoch"| S32["Slot 32
(Checkpoint)"]

6.2 区块提议 & 见证流程

sequenceDiagram participant R as RANDAO participant Prop as 区块提议者 participant Com as 见证委员会 participant Chain as Beacon Chain Note over R: 每个 Epoch 提前
伪随机选出所有 Slot 的
提议者和委员会 R->>Prop: 选中为当前 Slot 提议者 Prop->>Prop: 从执行层获取 Execution Payload Prop->>Prop: 封装 Beacon Block Prop->>Chain: 广播 Beacon Block R->>Com: 选出当前 Slot 见证委员会 Chain->>Com: 接收 Beacon Block Com->>Com: 重新执行验证 Com->>Chain: 投票 (Attestation) Note over Com: 投票包含:
1. 当前 Head 投票 (LMD-GHOST)
2. 源 Checkpoint 投票 (FFG)
3. 目标 Checkpoint 投票 (FFG)

6.3 Casper FFG 最终确定性

stateDiagram-v2 [*] --> Proposed: 区块被提出 Proposed --> Attested: 收集见证票 Attested --> Justified: 超过 2/3 投票
验证目标 Checkpoint Justified --> Finalized: 下一个 Epoch
再次超过 2/3 投票 Finalized --> [*] note right of Justified: 可能回滚 note right of Finalized: 不可回滚
需销毁 ≥1/3 总质押 ETH

6.4 LMD-GHOST 分叉选择

flowchart TB subgraph Fork["分叉场景"] Root["最新 Finalized 区块"] --> A["区块 A"] Root --> B["区块 B (竞争)"] A --> A1["A 的子区块"] B --> B1["B 的子区块"] end Choice["LMD-GHOST 规则"] -->|"选择"| Result["累积见证票权重
最高的分叉"] Note["每次只考虑
验证者的最新消息
(Latest Message)"]

6.5 验证者奖惩机制

flowchart TB subgraph Rewards["奖励"] R1["正确投见证票 → ETH 奖励"] R2["正确提议区块 → ETH 奖励"] R3["参与同步委员会 → ETH 奖励"] end subgraph Penalties["惩罚"] P1["离线/未投票 → 少量惩罚 (约等于错失的奖励)"] P2["双重提议 (Proposer Slashing) → 罚没 ≥1 ETH"] P3["矛盾见证 (Attester Slashing) → 罚没 ≥1 ETH"] P4["大规模罚没事件 → 相关惩罚,最多 100% 质押"] end subgraph Inactivity["怠工泄漏 (Inactivity Leak)"] I1["超过 4 个 Epoch 未 finalize 时触发"] I2["对不投票验证者逐步削减质押"] end

7. 状态存储:Merkle Patricia Trie

Ethereum 的全局状态存储在一个 Modified Merkle Patricia Trie (MPT) 中。每个区块头包含 stateRoot,唯一标识该区块时刻的完整世界状态。

7.1 三层 Trie 结构

graph TB BlockHeader["区块头"] -->|"stateRoot"| WorldState["世界状态 Trie
Key: 地址 → Value: 账户状态"] WorldState -->|"storageRoot"| StorageTrie1["合约 A 存储 Trie
Key: 存储槽位 → Value: 值"] WorldState -->|"storageRoot"| StorageTrie2["合约 B 存储 Trie"] subgraph TrieNode["Trie 节点类型"] Leaf["叶子节点
存实际 value"] Ext["扩展节点
存共享前缀"] Branch["分支节点
17 个子节点 (16 hex + 1 value)"] end

7.2 状态根更新流程

sequenceDiagram participant TX as 交易执行 participant EVM as EVM participant State as 世界状态 Trie TX->>EVM: 执行 SSTORE / 转账 EVM->>State: 读取当前状态 State-->>EVM: 返回当前值 EVM->>EVM: 计算新值 EVM->>State: 写入新值 (更新节点) Note over State: MPT 路径上的节点
被重新计算哈希
未被修改的节点共享 State-->>EVM: 返回新的存储根 EVM->>EVM: 更新账户 storageRoot Note over State: 最终生成新的
世界状态根 (stateRoot)
写入区块头

8. 节点架构

Ethereum 节点由执行客户端和共识客户端两个独立进程组成,通过本地 Engine API 通信。一个完整的验证者节点还需要运行验证者客户端。

8.1 节点组件

graph TB subgraph Node["Ethereum 节点"] subgraph EC["执行客户端 (Execution Client)"] EL_Engine["Engine API 端点"] EL_P2P["devp2p 网络"] EL_State[("状态数据库")] EL_BlockStore[("区块存储")] end subgraph CC["共识客户端 (Consensus Client)"] CL_Engine["Engine API 端点"] CL_P2P["libp2p 网络"] CL_Beacon[("Beacon 状态")] CL_FC["分叉选择"] end subgraph VC["验证者客户端 (Validator Client)"] V_Keys["BLS 密钥管理"] V_Duties["职责调度"] end EL_Engine <-->|"localhost
newPayload
forkchoiceUpdated"| CL_Engine V_Duties --> CL_Engine end EL_P2P <-->|"交易 & 区块 gossip"| Network["Ethereum P2P 网络"] CL_P2P <-->|"见证 & Beacon Block gossip"| Network

8.2 Engine API 核心方法

sequenceDiagram participant CL as 共识客户端 participant EL as 执行客户端 Note over CL,EL: ▸ 区块处理 CL->>EL: engine_newPayloadV3(executionPayload) EL->>EL: 验证 & 执行区块 EL-->>CL: PayloadStatus {VALID | INVALID | SYNCING} Note over CL,EL: ▸ 分叉选择更新 CL->>EL: engine_forkchoiceUpdatedV3(forkchoiceState, payloadAttributes) EL->>EL: 更新链头 EL-->>CL: ForkchoiceResponse {payloadStatus, payloadId} Note over CL,EL: ▸ 获取区块构建结果 CL->>EL: engine_getPayloadV3(payloadId) EL-->>CL: ExecutionPayload (含交易列表 & 状态根)

8.3 主流客户端实现

graph TB subgraph ExecClients["执行客户端"] Geth["Geth (Go) ← 主流"] Nethermind["Nethermind (C#)"] Besu["Besu (Java)"] Erigon["Erigon (Go)"] Reth["Reth (Rust)"] end subgraph ConsClients["共识客户端"] Lighthouse["Lighthouse (Rust) ← 主流"] Prysm["Prysm (Go)"] Teku["Teku (Java)"] Nimbus["Nimbus (Nim)"] Lodestar["Lodestar (TypeScript)"] end subgraph ValClients["验证者客户端"] Vouch["Vouch (Go) "] DVT["DVT 方案 (SSV/Obol)"] end

Ethereum System Architecture · 基于 ethereum.org 官方文档 · 2024